﻿// Copyright 2008 Rory Plaire (codekaizen@gmail.com)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using PerformanceDataRecorder;

namespace BubbleSortTestUnsafe
{
    class Program
    {
        private static readonly Int32 MaxItems = 66537;
        private static readonly Int32 ItemCount = 2;
        private static readonly Int32 Rate = 2;
        private static readonly Random _random = new Random();
        private static readonly Recorder _recorder = new Recorder("log.xml");

        static void Main(String[] args)
        {
            Int32 items = ItemCount;
            while (items < MaxItems)
            {
                Console.WriteLine("Bubble sorting " + items + " integer items.");
                runIntegerTests(items);
                items *= Rate;
            }

            Console.WriteLine("");
            items = ItemCount;
            while (items < MaxItems)
            {
                Console.WriteLine("Bubble sorting " + items + " single float items.");
                runSingleTests(items);
                items *= Rate;
            }

            Console.WriteLine("");
            items = ItemCount;
            while (items < MaxItems)
            {
                Console.WriteLine("Bubble sorting " + items + " double float items.");
                runDoubleTests(items);
                items *= Rate;
            }

            Console.WriteLine("Done.");
            Console.ReadKey();
        }

        private static void runIntegerTests(Int32 itemCount)
        {
            Stopwatch timer = new Stopwatch();

            timer.Reset();
            timer.Start();
            sortIntegerArray(itemCount);
            timer.Stop();
            _recorder.Log("Unsafe CLR stackalloc", typeof(Int32[]), typeof(Int32), itemCount, timer.Elapsed);
            Console.WriteLine("\tSorting array: " + timer.ElapsedMilliseconds + " ms.");

            timer.Reset();
            timer.Start();
            sortGenericIntegerList(itemCount);
            timer.Stop();
            _recorder.Log("Unsafe CLR pointer to private List<T> field", typeof(List<Int32>), typeof(Int32), itemCount, timer.Elapsed);
            Console.WriteLine("\tSorting generic list: " + timer.ElapsedMilliseconds + " ms.");

            timer.Reset();
            timer.Start();
            sortObjectIntegerList(itemCount);
            timer.Stop();
            _recorder.Log("Unsafe CLR pointer to private ArrayList field", typeof(ArrayList), typeof(Int32), itemCount, timer.Elapsed);
            Console.WriteLine("\tSorting object list: " + timer.ElapsedMilliseconds + " ms.");
        }

        private static void runSingleTests(Int32 itemCount)
        {
            Stopwatch timer = new Stopwatch();

            timer.Start();
            sortSingleArray(itemCount);
            timer.Stop();
            _recorder.Log("Unsafe CLR stackalloc", typeof(Single[]), typeof(Single), itemCount, timer.Elapsed);
            Console.WriteLine("\tSorting array: " + timer.ElapsedMilliseconds + " ms.");
        }

        private static void runDoubleTests(Int32 itemCount)
        {
            Stopwatch timer = new Stopwatch();

            timer.Start();
            sortDoubleArray(itemCount);
            timer.Stop();
            _recorder.Log("Unsafe CLR stackalloc", typeof(Double[]), typeof(Double), itemCount, timer.Elapsed);
            Console.WriteLine("\tSorting array: " + timer.ElapsedMilliseconds + " ms.");
        }

        private static void sortIntegerArray(Int32 itemCount)
        {
            unsafe
            {
                Int32* values = stackalloc Int32[itemCount];

                for (Int32 i = 0; i < itemCount; i++)
                {
                    values[i] = _random.Next();
                }

                sortIntegers(values, itemCount);
            }
        }

        private static void sortGenericIntegerList(Int32 itemCount)
        {
            List<Int32> values = new List<Int32>(itemCount);

            for (Int32 i = 0; i < itemCount; i++)
            {
                values.Add(_random.Next());
            }

            sortIntegers(values, itemCount);
        }

        private static void sortObjectIntegerList(Int32 itemCount)
        {
            ArrayList values = new ArrayList(itemCount);

            for (Int32 i = 0; i < itemCount; i++)
            {
                values.Add(_random.Next());
            }

            sortIntegers(values, itemCount);
        }

        private static void sortSingleArray(Int32 itemCount)
        {
            unsafe
            {
                Single* values = stackalloc Single[itemCount];

                for (Int32 i = 0; i < itemCount; i++)
                {
                    values[i] = (Single)_random.NextDouble();
                }

                sortSingles(values, itemCount);
            }
        }

        private static void sortDoubleArray(Int32 itemCount)
        {
            unsafe
            {
                Double* values = stackalloc Double[itemCount];

                for (Int32 i = 0; i < itemCount; i++)
                {
                    values[i] = _random.NextDouble();
                }

                sortDoubles(values, itemCount);
            }
        }

        private unsafe static void sortIntegers(Int32* values, Int32 length)
        {
            for (Int32 i = 0; i < length; i++)
            {
                for (Int32 j = 0; j < length - 1; j++)
                {
                    if (values[j] > values[j + 1])
                    {
                        Int32 tmp = values[j];
                        values[j] = values[j + 1];
                        values[j + 1] = tmp;
                    }
                }
            }
        }

        private unsafe static void sortIntegers(List<Int32> list, Int32 length)
        {
            FieldInfo itemsField =
                typeof(List<Int32>).GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic);

            Int32[] items = (Int32[])itemsField.GetValue(list);

            fixed (Int32* values = items)
            {
                for (Int32 i = 0; i < length; i++)
                {
                    for (Int32 j = 0; j < length - 1; j++)
                    {
                        if (values[j] > values[j + 1])
                        {
                            Int32 tmp = values[j];
                            values[j] = values[j + 1];
                            values[j + 1] = tmp;
                        }
                    }
                }
            }
        }

        private unsafe static void sortIntegers(ArrayList list, Int32 length)
        {
            FieldInfo itemsField =
                typeof(ArrayList).GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic);

            Object[] items = (Object[])itemsField.GetValue(list);

            //GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned);
            //Int32* values = (Int32*)handle.AddrOfPinnedObject().ToPointer();

            Int32* values = (Int32*)Marshal.UnsafeAddrOfPinnedArrayElement(items, 0).ToPointer();

            for (Int32 i = 0; i < length; i++)
            {
                for (Int32 j = 0; j < length - 1; j++)
                {
                    IntPtr boxed1 = new IntPtr(values[j]);
                    Int32 value1 = *(Int32*)new IntPtr((boxed1.ToInt32() + 4)).ToPointer();

                    IntPtr boxed2 = new IntPtr(values[j + 1]);
                    Int32 value2 = *(Int32*)new IntPtr((boxed2.ToInt32() + 4)).ToPointer();

                    if (value1 > value2)
                    {
                        Int32 tmp = values[j];
                        values[j] = values[j + 1];
                        values[j + 1] = tmp;
                    }
                }
            }

            //handle.Free();
        }

        private unsafe static void sortSingles(Single* values, Int32 length)
        {
            for (Int32 i = 0; i < length; i++)
            {
                for (Int32 j = 0; j < length - 1; j++)
                {
                    if (values[j] > values[j + 1])
                    {
                        Single tmp = values[j];
                        values[j] = values[j + 1];
                        values[j + 1] = tmp;
                    }
                }
            }
        }

        private unsafe static void sortDoubles(Double* values, Int32 length)
        {
            for (Int32 i = 0; i < length; i++)
            {
                for (Int32 j = 0; j < length - 1; j++)
                {
                    if (values[j] > values[j + 1])
                    {
                        Double tmp = values[j];
                        values[j] = values[j + 1];
                        values[j + 1] = tmp;
                    }
                }
            }
        }
    }
}
